<?php
declare(strict_types=1);

namespace WebApp\lib\db\mysql;

use ErrorException;
use PDOException;
use Swoole\Coroutine\Mysql;
use WebApp\lib\util\Util;

/**
 * Class CoTable
 * @package WebApp\lib\db
 */
class CoTable extends MysqlTable implements ITable
{
    public function __construct()
    {
        Util::logD("CoTable using==============================");
    }

    /**
     * @param $arr
     * @return mixed
     */
    public function transaction($arr)
    {
        return function (Mysql $client) use (&$arr) {
            $client->query('begin transition');
            try {
                foreach ($arr as $res) {
                    if (gettype($res) !== 'object') {
                        throw new ErrorException();
                    }
                    $res = (bool)$res();
                    if (!$res) {
                        throw new ErrorException();
                    }
                }
                $client->query('commit transition');
                return true;
            } catch (PDOException $e) {
                $client->query('rollback transition');
                throw $e;
            } catch (ErrorException $e) {
                $client->query('rollback transition');
                throw $e;
            }
        };
    }

    /**
     * @param string $sql
     * @return mixed
     */
    public function getObjArrBySql(string $sql)
    {

        $this->setLastSql($sql);
        return function (Mysql $client) {
            return $client->query($this->getLastSql());
        };
    }

    /**
     * @param string $sql
     * @return mixed
     */
    public function getObjBySql(string $sql)
    {
        $this->setLastSql($sql);
        return function (Mysql $client) {
            $res = $client->query($this->getLastSql());
            if ($res) {
                return $res[0];
            } else {
                return [];
            }
        };
    }

    /**
     * @param $db_name
     * @param array $data
     * @param array $where
     * @return mixed
     */
    public function updateObj($db_name, $data = [], $where = [])
    {
        return function (Mysql $client) use (&$db_name, &$data, &$where) {
            $str = $this->makeWhere($where);
            $set = $this->makeSetData($data);
            $sql = "UPDATE $db_name $set $str";
            $this->setLastSql($sql);
            return $client->query($sql);
        };
    }

    /**
     * @param $db_name
     * @param array $where
     * @return mixed
     */
    public function removeObj($db_name, $where = [])
    {
        return function (Mysql $client) use (&$db_name, &$where) {
            $str = $this->makeWhere($where);
            $this->setLastSql("DELETE FROM $db_name $str");
            return $client->query($this->getLastSql());
        };
    }

    /**
     * @param $db_name
     * @param array $data
     * @return mixed
     */
    public function replaceObj($db_name, $data = [])
    {
        return function (Mysql $client) use (&$db_name, &$data) {
            $set = $this->makeSetData($data);
            $this->setLastSql("REPLACE INTO $db_name $set");
            return $client->query($this->getLastSql());
        };
    }

    /**
     * @param $db_name
     * @param array $data
     * @return mixed
     */
    public function addObj($db_name, $data = [])
    {
        return function (Mysql $client) use (&$db_name, &$data) {
            $str = $this->makeSetData($data);
            $this->setLastSql("INSERT INTO $db_name $str");
            $res = $client->query($this->getLastSql());
            if (!$res) {
                return -1;
            } else {
                return $client->insert_id;
            }
        };
    }

    /**
     * @param $db_name
     * @param array $where
     * @return mixed
     */
    public function getObjCount($db_name, $where = [])
    {
        return  function (Mysql $client) use (&$db_name, &$where) {
            $str = $this->makeWhere($where);
            $this->setLastSql("SELECT count(*) as c FROM $db_name $str");
            $res = $client->query($this->getLastSql());
            if ($res) {
                return $res[0]['c'];
            } else {
                return false;
            }
        };
    }

    /**
     * @param $db_name
     * @param array $need
     * @param array $where
     * @param string $group
     * @param string $sort
     * @param string $limit
     * @return mixed
     */
    public function getObjArr($db_name, $need = [], $where = [], $group = '', $sort = '', $limit = '')
    {
        return function (Mysql $client) use (&$db_name, &$need, &$where, &$group, &$sort, &$limit) {
            $str = $this->makeWhere($where);
            $group = $this->makeGroup($group);
            $sort = $this->makeOrder($sort);
            $limit = $this->makeLimit($limit);
            $need = $this->makeNeed($need);
            $this->setLastSql("SELECT $need FROM $db_name $str $group $sort $limit");

            return $client->query($this->getLastSql());
        };
    }

    /**
     * @param $db_name
     * @param array $need
     * @param array $where
     * @param string $group
     * @param string $sort
     * @return mixed
     */
    public function getObj($db_name, $need = [], $where = [], $group = '', $sort = '')
    {
        return function (Mysql $client) use (&$db_name, &$need, &$where, &$group, &$sort) {
            $str = $this->makeWhere($where);
            $group = $this->makeGroup($group);
            $sort = $this->makeOrder($sort);
            $need = $this->makeNeed($need);
            $this->setLastSql("SELECT $need FROM $db_name $str $group $sort limit 1");
            $rows = $client->query($this->getLastSql());
            if ($rows) {
                return $rows[0];
            } else {
                return $rows;
            }
        };
    }

    /**
     * @param $data
     * @return mixed
     */
    public function makeSetData(&$data)
    {
        if ($data && is_array($data)) {
            $str = 'SET ';
            foreach ($data as $k => $v) {
                if (is_string($v)) {
                    $v = "'$v'";
                }
                $str .= "`{$k}`=$v,";
            }
            $str = substr($str, 0, -1);
            return $str;
        } else {
            return '';
        }
    }

    /**
     * @param $where
     * @return mixed
     */
    public function makeWhere(&$where)
    {
        if (is_array($where) && $where) {
            $str = 'WHERE ';
            foreach ($where as $k => $v) {
                if (is_array($v)) {
                    $symbol = $v[0];
                    $v = $v[1];
                } else {
                    $symbol = '=';
                }
                if (is_string($v)) {
                    $v = "'" . $v . "'";
                }
                $str .= "`$k`" . $symbol . $v . " AND ";
            }
            $str = substr($str, 0, -5);
            return $str;
        } else {
            return '';
        }
    }

    /**
     * @param string $_sql
     * @return mixed
     */
    public function execute(string $_sql)
    {
        $this->setLastSql($_sql);
        return function (Mysql $client) {
            return $client->query($this->getLastSql());
        };
    }

    public function getLastSql()
    {
        // TODO: Implement getLastSql() method.
    }

    public function getLastId()
    {
        // TODO: Implement getLastId() method.
    }

    public function getLastErrInfo(): string
    {
        // TODO: Implement getLastErrInfo() method.
    }

    public function getLastErrCode(): string
    {
        // TODO: Implement getLastErrCode() method.
    }

    public function setLastSql($sql)
    {
        // TODO: Implement setLastSql() method.
    }

    public function setLastId($id)
    {
        // TODO: Implement setLastId() method.
    }

    public function setLastErrInfo(string $info)
    {
        // TODO: Implement setLastErrInfo() method.
    }

    public function setLastErrCode($code)
    {
        // TODO: Implement setLastErrCode() method.
    }
}
